﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FrmThreadControl
{
    /// <summary>
    /// 
    /// 如果从另外一个线程（比如自己开的子线程）操作Windows窗体上的控件，就会和主线程产生竞争，从而出现异常，甚至死锁。
    /// 所以，Windows编程有一个规则：就是控件的更新，必须是创建这个控件的线程才可以，其他线程，不能直接操作
    /// 
    /// 所以，如果从其他的子线程中访问窗体控件，就需要借助与Invoke或BeginInvoke方法，通过一个“委托”把“要访问的这个过程”
    /// 传送到控件所在的主线程上去执行
    /// 
    /// 
    /// 总结
    /// 第一、使用Invoke完成一个委托方法的传送，是同步方法，也就是Invoke封送方法后，不会马上返回，需要等待委托方法执行结束，才
    /// 执行当前线程(调用者)后面的内容。
    /// 
    /// 第二、使用BeginInvoke完成一个委托方法的传送，是异步方法。也就是BeginInvoke封送方法后，马上返回，不会等待委托方法执行结束，
    /// 就会执行当前线程（调用者）后面的内容。
    /// 
    /// 第三、控件的InvokeRequired属性用来在编程的时候确定，当前控件是否需要用Invoke来进行封送。如果不需要，可以直接更新
    /// 
    /// 
    /// Invoke和BeginInvoke方法的选择
    /// 第一：如果委托方法后面有其他的代码需要执行，并且需要用到委托方法执行完以后的数据，那就需要等待，这时候，就要选择Invoke()方法，
    /// 否则，使用BeginInvoke会导致数据不一致的情况。
    /// 
    /// 第二：使用委托方法后面有其他的代码需要执行，但是和委托方法执行没有任何关联。
    /// --》如果委托方法执行很快，用Invoke和BeginInvoke没有什么区别
    /// --》如果委托方法执行耗时，则用BeginInvoke异步，防止线程阻塞
    /// 
    /// Invoke()或BeginInvoke()的作用是：在应用程序的主程序上执行指定的委托
    /// 
    /// 【4】我们也经常看到使用this.Invoke(),而上面我们用的是Control.Invoke(),有什么说法？
    /// this替换为窗体任何一个控件都是OK的，因为Control.Invoke含义是将方法委托给拥有该Control的线程去执行。
    /// 
    /// 更简单的，this都可以不写！！！
    /// 
    /// 【5】委托Delegate中的Invoke（）方法
    /// 这个Invoke仅仅表示调用委托而已，而且是同步方法，也就是如果耗时会卡住。当然实际应用中，我们也可以不用Invoke()，而且
    /// 直接使用委托变量。
    /// 
    /// </summary>
    public partial class FrmThreadControl : Form
    {
        public FrmThreadControl()
        {
            InitializeComponent();
        }

        //多线程执行任务1
        private void btnExecute1_Click(object sender, EventArgs e)
        {
            Thread thread = new Thread(() =>
            {
                //this.lblResult1.Text = "100";

                //比如在这里可以访问下位机或数据，或其他形式，获取数据
                string datas = "100";

                /*this.lblResult1.Invoke(new Action<string>(data =>
                {
                    this.lblResult1.Text = data;
                }),"100"); //字符串100，就是要传递给控件lblResult显示用的data变量的值，这个地方也可以使用其他变量

                //如果上面的Invoke方法，下面的代码会等待Invoke内容执行完毕后才执行
                Console.WriteLine("****************************************");
                Console.WriteLine("****************************************");*/


               /* this.lblResult1.BeginInvoke(new Action<string>(data =>
                {
                    this.lblResult1.Text = data;
                }), "100");*/

                VisitControls(datas);   //这个是在线程中调用一个独立的方法，需要注意判断是否在一个线程中

                //如果上面的BeginInvoke方法，下面的代码不会等待BeginInvoke内容执行完毕,会立即执行
                Console.WriteLine("****************************************");
                Console.WriteLine("****************************************");

            });
            thread.IsBackground = true;  //设置背景线程
            thread.Start();
        }

        //这个方法可以被主线程访问，也可以被子线程访问
        private void VisitControls(string datas)
        {
            if(this.lblResult1.InvokeRequired)  //如果是子线程访问
            {
                this.lblResult1.BeginInvoke(new Action<string>(data =>
                {
                    this.lblResult1.Text = data;
                }), datas);
            }
            else   //如果是主线程访问
            {
                this.lblResult1.Text = datas;
            }        
        }

        //多线程执行任务2
        private void btnExecute2_Click(object sender, EventArgs e)
        {
            Thread thread = new Thread(() =>
            {
                string outdata1 = "100";
                string outdata2 = "200";

                //this.Invoke()
                Invoke(new Action<string, string>((data1, data2) =>
                {
                    this.lblResult2.Text = data1;
                    this.lblResult3.Text = data2;
                }),outdata1,outdata2);
            });
            thread.IsBackground = true;
            thread.Start();
        }

        //多线程读取数据(来自于控件)
        private void btnRead_Click(object sender, EventArgs e)
        {
            Thread thread1 = new Thread(() =>
            {
                this.Invoke(new Action<string>(data =>
                {
                    this.lblV.Text = data;

                    //读取到的data也可以发送给下位机.....

                }),this.txtV.Text.Trim());
            });
            thread1.IsBackground = true;
            thread1.Start();
        }
    }
}
